/****************************************************************************************************
 * 项目：垃圾回收
 * 描述：。
 ****************************************************************************************************/
#include "zhi.h"
#if DEBUG
   #include "debug.h"
   #include <time.h>
#endif

//标灰obj:即把obj收集到数组vm->grays.grayObjects
void 标灰对象(虚拟机* 虚拟机S, 对象头S* obj) {
   //如果isDark为true表示为黑色,说明已经可达,直接返回
   if (obj == NULL || obj->对象是否可达) return;

   //标记为可达
   obj->对象是否可达 = true; 

   //若超过了容量就扩容
   if (虚拟机S->grays.count >= 虚拟机S->grays.数组容量) {
      虚拟机S->grays.数组容量 = 虚拟机S->grays.count * 2;
      虚拟机S->grays.grayObjects = 
	 (对象头S**)realloc(虚拟机S->grays.grayObjects, 虚拟机S->grays.数组容量 * sizeof(对象头S*));
   }

   //把obj添加到数组grayObjects
   虚拟机S->grays.grayObjects[虚拟机S->grays.count++] = obj;
}

//标灰value
void 标灰值(虚拟机* 虚拟机S, Value value) {
   //只有对象才需要标记
   if (!VALUE_IS_OBJ(value)) {
      return;
   }
   标灰对象(虚拟机S, VALUE_TO_OBJ(value));
}

//标灰buffer->datas中的value
static void 标灰缓冲(虚拟机* 虚拟机S, ValueBuffer* buffer) {
   uint32_t idx = 0;
   while (idx < buffer->count) {
      标灰值(虚拟机S, buffer->数据缓冲区[idx]);
      idx++;
   }
}

//标黑class
static void 标黑类(虚拟机* 虚拟机S, Class* class) {
   //标灰meta类
   标灰对象(虚拟机S, (对象头S*)class->objHeader.class);

   //标灰父类
   标灰对象(虚拟机S, (对象头S*)class->superClass);

   //标灰方法
   uint32_t idx = 0;
   while (idx < class->methods.count) {
      if (class->methods.数据缓冲区[idx].type == 方法类型_脚本) {
	 标灰对象(虚拟机S, (对象头S*)class->methods.数据缓冲区[idx].obj);
      }
      idx++;
   }

   //标灰类名
   标灰对象(虚拟机S, (对象头S*)class->name);

   //累计类大小
   虚拟机S->系统已分配的总内存 += sizeof(Class);
   虚拟机S->系统已分配的总内存 += sizeof(Method) * class->methods.数组容量;
}

//标灰闭包
static void 标黑闭包(虚拟机* 虚拟机S, 闭包对象S* objClosure) {
   //标灰闭包中的函数
   标灰对象(虚拟机S, (对象头S*)objClosure->fn);

   //标灰包中的upvalue
   uint32_t idx = 0;
   while (idx < objClosure->fn->upvalueNum) {
      标灰对象(虚拟机S, (对象头S*)objClosure->upvalues[idx]);
      idx++;
   }

   //累计闭包大小
   虚拟机S->系统已分配的总内存 += sizeof(闭包对象S);
   虚拟机S->系统已分配的总内存 += sizeof(上一层对象S*) * objClosure->fn->upvalueNum;
}

//标黑objThread
static void 标黑线程(虚拟机* 虚拟机S, 线程对象S* objThread) {
   //标灰frame
   uint32_t idx = 0;
   while (idx < objThread->usedFrameNum) {
      标灰对象(虚拟机S, (对象头S*)objThread->frames[idx].closure);
      idx++;
   }

   //标灰runtime栈中每个slot
   Value* slot =  objThread->stack;
   while (slot < objThread->esp) {
      标灰值(虚拟机S, *slot);
      slot++; 
   }

   //标灰本线程中所有的upvalue
   上一层对象S* upvalue = objThread->openUpvalues;
   while (upvalue != NULL) {
      标灰对象(虚拟机S, (对象头S*)upvalue);
      upvalue = upvalue->next;
   }

   //标灰caller
   标灰对象(虚拟机S, (对象头S*)objThread->caller);
   标灰值(虚拟机S, objThread->errorObj);

   //累计线程大小
   虚拟机S->系统已分配的总内存 += sizeof(线程对象S);
   虚拟机S->系统已分配的总内存 += objThread->frameCapacity * sizeof(调用框架S);
   虚拟机S->系统已分配的总内存 += objThread->stackCapacity * sizeof(Value);
}

//标黑fn
static void 标黑函数(虚拟机* 虚拟机S, 函数对象S* fn) {
   //标灰常量
   标灰缓冲(虚拟机S, &fn->constants);

   //累计Objfn的空间
   虚拟机S->系统已分配的总内存 += sizeof(函数对象S);
   虚拟机S->系统已分配的总内存 += sizeof(uint8_t) * fn->instrStream.数组容量;
   虚拟机S->系统已分配的总内存 += sizeof(Value) * fn->constants.数组容量;
  
#if DEBUG  
   //再加上debug信息占用的内存
   虚拟机S->系统已分配的总内存 += sizeof(Int) * fn->instrStream.数组容量;
#endif  
}

//标黑objInstance
static void 标黑对象实例(虚拟机* 虚拟机S, 对象实例S* objInstance) {
   //标灰元类
   标灰对象(虚拟机S, (对象头S*)objInstance->objHeader.class);

   //标灰实例中所有域,域的个数在class->fieldNum
   uint32_t idx = 0;
   while (idx < objInstance->objHeader.class->fieldNum) {
      标灰值(虚拟机S, objInstance->fields[idx]);
      idx++;
   }

   //累计objInstance空间
   虚拟机S->系统已分配的总内存 += sizeof(对象实例S);
   虚拟机S->系统已分配的总内存 += sizeof(Value) * objInstance->objHeader.class->fieldNum;
}

//标黑objList
static void 标黑list(虚拟机* 虚拟机S, list对象S* objList) {
   //标灰list的elements
   标灰缓冲(虚拟机S, &objList->elements);

   //累计objList大小
   虚拟机S->系统已分配的总内存 += sizeof(list对象S);
   虚拟机S->系统已分配的总内存 += sizeof(Value) * objList->elements.数组容量;
}

//标黑objMap
static void 标黑map(虚拟机* 虚拟机S, map对象S* objMap) {
   //标灰所有entry
   uint32_t idx = 0;
   while (idx < objMap->数组容量) {
      Entry* entry = &objMap->entries[idx];
      //跳过无效的entry
      if (!VALUE_IS_UNDEFINED(entry->key)) {
	 标灰值(虚拟机S, entry->key);
	 标灰值(虚拟机S, entry->value);
      }
      idx++;
   }

   //累计ObjMap大小
   虚拟机S->系统已分配的总内存 += sizeof(map对象S);
   虚拟机S->系统已分配的总内存 += sizeof(Entry) * objMap->数组容量;
}

//标黑objModule
static void 标黑模块(虚拟机* 虚拟机S, 模块对象S* objModule) {
   //标灰模块中所有模块变量
   uint32_t idx = 0;
   while (idx < objModule->moduleVarValue.count) {
      标灰值(虚拟机S, objModule->moduleVarValue.数据缓冲区[idx]);
      idx++;
   }

   //标灰模块名
   标灰对象(虚拟机S, (对象头S*)objModule->name);

   //累计ObjModule大小
   虚拟机S->系统已分配的总内存 += sizeof(模块对象S);
   虚拟机S->系统已分配的总内存 += sizeof(ZiFuChuan) * objModule->moduleVarName.数组容量;
   虚拟机S->系统已分配的总内存 += sizeof(Value) * objModule->moduleVarValue.数组容量;
}

//标黑range
static void 标黑范围(虚拟机* 虚拟机S) {
   //ObjRange中没有大数据,只有from和to,
   //其空间属于sizeof(范围对象S),因此不用额外标记
   虚拟机S->系统已分配的总内存 += sizeof(范围对象S);
}

//标黑objZiFuChuan
static void 标黑字符串(虚拟机* 虚拟机S, 字符串对象S* objZiFuChuan) {
   //累计ObjZiFuChuan空间 +1是结尾的'\0'
   虚拟机S->系统已分配的总内存 += sizeof(字符串对象S) + objZiFuChuan->value.长度 + 1;
}

//标黑objUpvalue
static void 标黑上一层值(虚拟机* 虚拟机S, 上一层对象S* objUpvalue) {
   //标灰objUpvalue的closedUpvalue
   标灰值(虚拟机S, objUpvalue->closedUpvalue);

   //累计objUpvalue大小
   虚拟机S->系统已分配的总内存 += sizeof(上一层对象S);
}

//标黑obj
static void 标黑对象(虚拟机* 虚拟机S, 对象头S* obj) {
#ifdef DEBUG
   printf("mark ");
   Value vTmp;
   SET_VALUE_OBJ(&vTmp, obj);
   dumpValue(vTmp);
   //dumpValue(OBJ_TO_VALUE(obj));
   printf(" @ %p\n", obj);
#endif
//根据对象类型分别标黑
   switch (obj->type) {
      case 对象类型_类:
	 标黑类(虚拟机S, (Class*)obj);
	 break;
       
      case 对象类型_闭包:
	 标黑闭包(虚拟机S, (闭包对象S*)obj);
	 break;

      case 对象类型_线程:
	 标黑线程(虚拟机S, (线程对象S*)obj);
	 break;

      case 对象类型_函数:
	 标黑函数(虚拟机S, (函数对象S*)obj);
	 break;

      case 对象类型_对象实例:
	 标黑对象实例(虚拟机S, (对象实例S*)obj);
	 break;

      case 对象类型_列表:
	 标黑list(虚拟机S, (list对象S*)obj);
	 break;

      case 对象类型_散列数组:
	 标黑map(虚拟机S, (map对象S*)obj);
	 break;

      case 对象类型_模块作用域:
	 标黑模块(虚拟机S, (模块对象S*)obj);
	 break;

      case 对象类型_范围:
	 标黑范围(虚拟机S);
	 break;

      case 对象类型_字符串:
	 标黑字符串(虚拟机S, (字符串对象S*)obj);
	 break;

      case 对象类型_自由变量: 
	 标黑上一层值(虚拟机S, (上一层对象S*)obj);
	 break;
   }
}

//标黑那些已经标灰的对象,即保留那些标灰的对象
static void 标黑那些已经标灰的对象(虚拟机* 虚拟机S) {
//所有要保留的对象都已经收集到了vm->grays.grayObjects中,
//现在逐一标黑
   while (虚拟机S->grays.count > 0) {
      对象头S* objHeader = 虚拟机S->grays.grayObjects[--虚拟机S->grays.count];
      标黑对象(虚拟机S, objHeader);
   }
}

//释放obj自身及其占用的内存
void 释放对象自身及其占用的内存(虚拟机* 虚拟机S, 对象头S* obj) {
#ifdef DEBUG 
   printf("free ");
   Value vTmp;
   SET_VALUE_OBJ(&vTmp, obj);
   dumpValue(vTmp);
   //dumpValue(OBJ_TO_VALUE(obj));
   printf(" @ %p\n", obj);
#endif

//根据对象类型分别处理
   switch (obj->type) { 
      case 对象类型_类:
	 MethodBufferClear(虚拟机S, &((Class*)obj)->methods);
	 break;

      case 对象类型_线程: {
	 线程对象S* objThread = (线程对象S*)obj;
	 释放内存M(虚拟机S, objThread->frames);
	 释放内存M(虚拟机S, objThread->stack);
	 break;
      }
		      
      case 对象类型_函数: {
	 函数对象S* fn = (函数对象S*)obj;
	 ValueBufferClear(虚拟机S, &fn->constants);
	 ByteBufferClear(虚拟机S, &fn->instrStream);
      #if DEBUG
	 IntBufferClear(虚拟机S, &fn->debug->行号);
	 释放内存M(虚拟机S, fn->debug->fnName);
	 释放内存M(虚拟机S, fn->debug);
      #endif
	 break;
      }

      case 对象类型_列表:
	 ValueBufferClear(虚拟机S, &((list对象S*)obj)->elements);
	 break;

      case 对象类型_散列数组:
	 释放内存M(虚拟机S, ((map对象S*)obj)->entries);
	 break;

      case 对象类型_模块作用域:
	 ZiFuChuanBufferClear(虚拟机S, &((模块对象S*)obj)->moduleVarName);
	 ValueBufferClear(虚拟机S, &((模块对象S*)obj)->moduleVarValue);
	 break;

       case 对象类型_字符串:
       case 对象类型_范围:
       case 对象类型_闭包:
       case 对象类型_对象实例:
       case 对象类型_自由变量:
	 break;
   }

   //最后再释放自己
   释放内存M(虚拟机S, obj);
}

//立即运行垃圾回收器去释放未用的内存
void 运行垃圾回收器(虚拟机* 虚拟机S) {
#ifdef DEBUG 
   double startTime = (double)clock() / CLOCKS_PER_SEC;
   uint32_t before = 虚拟机S->系统已分配的总内存;
   printf("-- gc  before:%d   nextGC:%d  虚拟机S:%p  --\n",
	 before, 虚拟机S->config.nextGC, 虚拟机S);
#endif
// 一 标记阶段:标记需要保留的对象

   //将allocatedBytes置0便于精确统计回收后的总分配内存大小
   虚拟机S->系统已分配的总内存 = 0;

  //allModules不能被释放
   标灰对象(虚拟机S, (对象头S*)虚拟机S->allModules);

   //标灰tmpRoots数组中的对象(不可达但是不想被回收,白名单)
   uint32_t idx = 0;
   while (idx < 虚拟机S->tmpRootNum) {
      标灰对象(虚拟机S, 虚拟机S->临时的根对象集合[idx]);
      idx++;
   }

   //标灰当前线程,不能被回收
   标灰对象(虚拟机S, (对象头S*)虚拟机S->当前正在执行的线程);

   //编译过程中若申请的内存过高就标灰编译单元
   if (虚拟机S->当前词法分析器 != NULL) {
      ASSERT(虚拟机S->当前词法分析器->当前编译单元 != NULL, 
	    "编译单元可达 only be called while compiling!");
      编译单元可达(虚拟机S, 虚拟机S->当前词法分析器->当前编译单元);  
   }

   //置黑所有灰对象(保留的对象)
   标黑那些已经标灰的对象(虚拟机S);  

// 二 清扫阶段:回收白对象(垃圾对象)

   对象头S** obj = &虚拟机S->所有已分配对象链表;
   while (*obj != NULL) { 
      //回收白对象
      if (!((*obj)->对象是否可达)) {
	 对象头S* unreached = *obj;
	 *obj = unreached->next;
	 释放对象自身及其占用的内存(虚拟机S, unreached);
      } else {
	 //如果已经是黑对象,为了下一次gc重新判定,
	 //现在将其恢复为未标记状态,避免永远不被回收
	 (*obj)->对象是否可达 = false;
	 obj = &(*obj)->next;
      }
   }

   //更新下一次触发gc的阀值
   虚拟机S->config.nextGC = 虚拟机S->系统已分配的总内存 * 虚拟机S->config.heapGrowthFactor;
   if (虚拟机S->config.nextGC < 虚拟机S->config.最小堆大小) {
      虚拟机S->config.nextGC = 虚拟机S->config.最小堆大小;
   }

#ifdef DEBUG
   double elapsed = ((double)clock() / CLOCKS_PER_SEC) - startTime;
   printf("GC %lu before, %lu after (%lu collected), next at %lu. take %.3fs.\n",
	 (unsigned long)before,
	 (unsigned long)虚拟机S->系统已分配的总内存,
	 (unsigned long)(before - 虚拟机S->系统已分配的总内存),
	 (unsigned long)虚拟机S->config.nextGC,
	 elapsed);
#endif
}
